package com.geeksss.handler;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * 基本语法
 * <p>
 * 1.使用注解:
 *
 * @OnceAbsoluteMerge( firstRowIndex = 5, lastRowlndex = 6，firstcolumnIndex = 1，lastcolumnindex = 2)
 * public class 模型映射类 {}
 * @ContentLoopMerge(eachRow=2) private String fieldName;
 * <p>
 * 2.自定义策略
 * 循环合并: new LoopMergestrategy( 2，columnIndex:0 )
 * 合并一次: new OnceAbsoluteMergeStrategy(firstRowindex: 0, lastRowindex: 1, firstcolumnindex: 4, lastcolumnindex: 4);
 * 使用策略:EasyExcel.write(file)
 * .head(映射模型.class)
 * .sheet()
 * .registerWriteHandler(合并策略)
 * .doWrite(数据);
 */

/**
 * 自定义合并策略类，用于实现单元格的特定合并逻辑。
 * 该类作为写入处理器，会在单元格写入后进行处理，判断是否需要合并单元格。
 */
public class CustomMergeStrategy implements CellWriteHandler {
    
    
    /**
     * 需要合并的列名列表。
     * 这个列表决定了哪些列的相同值单元格会被合并。
     */
    /**
     * 合并列名。
     */
    private final List<String> mergeColumnNames;
    
    /**
     * 构造函数，初始化需要合并的列名列表。
     *
     * @param mergeColumnNames 需要合并的列名列表。
     */
    /**
     * 构造函数。
     *
     * @param mergeColumnNames 合并列名。
     */
    public CustomMergeStrategy(List<String> mergeColumnNames) {
        this.mergeColumnNames = mergeColumnNames;
    }
    
    /**
     * 合并逻辑：如果 当前单元格与上一行同列的单元格一致则进行合并
     *
     * 在单元格写入后进行处理的接口方法。
     *
     * @param writeSheetHolder 包含写入表格信息的持有者。
     * @param writeTableHolder 包含写入表格数据的持有者。
     * @param cellDataList     单元格数据列表。
     * @param cell             当前处理的单元格。
     * @param head             当前单元格对应的表头信息。
     * @param relativeRowIndex 当前单元格的相对行索引。
     * @param isHead           标志位，表示当前单元格是否为表头。
     */
    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 校验：如果当前是表头，则不处理。
        if (isHead) {
            return;
        }
        // 校验：如果当前是第一行，则不处理。 relativeRowIndex是已经排除掉标头的行数据
        if (relativeRowIndex == 0) {
            return;
        }
        // 校验：如果当前列名不在要合合并列名列表中，则不处理。
        if (!this.mergeColumnNames.contains(head.getFieldName())) {
            return;
        }
        
        // 当前单元格与上一行单元格的列是一致的
        // 获取：当前上一行同列单元格
        Sheet sheet = cell.getSheet();
        // 当前行索引
        int rowIndexCurrent = cell.getRowIndex();
        // 前一行索引
        int rowIndexPrev = rowIndexCurrent - 1;
        // 前一整行行单元格
        Row rowPrev = sheet.getRow(rowIndexPrev);
        // 前一列单元格
        Cell cellPrev = rowPrev.getCell(cell.getColumnIndex());
        
        // 获取：当前单元格值、上一单元格值。根据单元格类型决定获取字符串值还是数字值
        Object cellValueCurrent = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Object cellValuePrev = cellPrev.getCellType() == CellType.STRING ? cellPrev.getStringCellValue() : cellPrev.getNumericCellValue();
        
        // 校验：如果当前单元格值与上一单元格值不相等，则不处理。
        if (!cellValueCurrent.equals(cellValuePrev)) {
            return;
        }
        
        // 获取当前工作表中已存在的合并区域列表
        List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
        boolean merged = false;
        // 遍历已存在的合并区域，如果当前单元格在已存在的合并区域中，则调整合并区域的范围，否则新增一个合并区域
        for (int i = 0; i < mergedRegions.size(); i++) {
            CellRangeAddress cellRangeAddress = mergedRegions.get(i);
            if (cellRangeAddress.isInRange(rowIndexPrev, cell.getColumnIndex())) {
                // 如果当前单元格在已合并区域中，则调整合并区域的范围
                sheet.removeMergedRegion(i); // 移除合并单元格。
                // 重新进行合并
                cellRangeAddress.setLastRow(rowIndexCurrent); // 设置合并单元格的结束行。
                sheet.addMergedRegion(cellRangeAddress); // 重新添加合并单元格。
                merged = true;
                break;
            }
        }
        if (!merged) {
            // 如果当前单元格不在已合并区域中，则新增一个合并区域
            CellRangeAddress cellRangeAddress = new CellRangeAddress(rowIndexPrev, rowIndexCurrent, cell.getColumnIndex(), cell.getColumnIndex());
            sheet.addMergedRegion(cellRangeAddress);
        }
    }
}
